package me.jzn.alib.utils;

import android.Manifest;
import android.Manifest.permission;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.LocaleList;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresPermission;
import androidx.core.content.ContextCompat;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import me.jzn.alib.ALib;
import me.jzn.alib.inner.InnerData;
import me.jzn.alib.pref.Pref;
import me.jzn.core.Core;
import me.jzn.core.exceptions.ShouldNotRunHereException;
import me.jzn.core.utils.ByteUtil;
import me.jzn.core.utils.HashUtil;
import me.jzn.core.utils.KeyStoreUtil;
import me.jzn.core.utils.StrUtil;

/**
 * @author xqs
 * 用来查询一些安卓特有的信息
 */
public class AndrUtil {
    private static final Logger log = LoggerFactory.getLogger(AndrUtil.class);

    public static final boolean isDebugMode() {
        return isDebugMode(ALib.app());
    }

    /**
     * @param sdkVer Build.VERSION.SDK_INT/Build.VERSION_CODES.M
     */
    public static String sdkVersionMap(int sdkVer) {
        return InnerData.sdkVersionStr(sdkVer);
    }

    public static final boolean isDebugMode(Context ctx) {
        ApplicationInfo appInfo = ctx.getApplicationInfo();
        if (appInfo != null) {
            return ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
        }
        return true;
    }

    // 开发者选项是否开启
    public static final boolean isDevelopOpened() {
        ContentResolver cr = ALib.app().getContentResolver();
        String key = Settings.Global.DEVELOPMENT_SETTINGS_ENABLED;
        if (Build.VERSION.SDK_INT >= 17) {
            return Settings.Global.getInt(cr, key, 0) != 0;
        } else {
            return Settings.Secure.getInt(cr, key, 0) != 0;
        }
    }

    /**
     * 检查manifest中是否申请了QUERY_ALL_PACKAGES权限。
     * 但是如果写了，但是用户关闭了，仍然返回true
     */
    public static boolean supportQueryAllPackage() {
        if (Build.VERSION.SDK_INT < 30) {
            //安卓11以上才检查该权限
            return true;
        }
        //实际上这个返回并不准确，只要manifest中写了，这个就返回true，但是如果用户手动关了，仍然返回true
        return AndrUtil.hasPermission(Manifest.permission.QUERY_ALL_PACKAGES);
    }

    @NonNull
    @SuppressLint("MissingPermission")
    public static final ApplicationInfo getAppInfo() {
        return getAppInfo(ALib.app().getPackageName());
    }

    /**
     * queris or permission.QUERY_ALL_PACKAGES
     */
    @Nullable
    @RequiresPermission(permission.QUERY_ALL_PACKAGES)
    public static final ApplicationInfo getAppInfo(String pkgName) {
        PackageManager pkgmgr = ALib.app().getPackageManager();
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                return pkgmgr.getApplicationInfo(pkgName, PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA));
            } else {
                return pkgmgr.getApplicationInfo(pkgName, PackageManager.GET_META_DATA);
            }
        } catch (NameNotFoundException e) {
            return null;
        }
    }

    @RequiresPermission(permission.QUERY_ALL_PACKAGES)
    public static final List<ApplicationInfo> getAllAppInfos() {
        PackageManager pm = ALib.app().getPackageManager();
        return pm.getInstalledApplications(PackageManager.GET_META_DATA);
    }

    @NonNull
    @SuppressLint("MissingPermission")
    public static PackageInfo getPkgInfo() {
        return getPkgInfo(ALib.app().getPackageName());
    }

    /**
     * queris or permission.QUERY_ALL_PACKAGES
     */
    @Nullable
    @RequiresPermission(permission.QUERY_ALL_PACKAGES)
    public static PackageInfo getPkgInfo(String pkgName) {
        PackageManager pkgmgr = ALib.app().getPackageManager();
        try {
            return pkgmgr.getPackageInfo(pkgName, PackageManager.GET_CONFIGURATIONS);
        } catch (NameNotFoundException e) {
            return null;
        }
    }

    public static boolean hasPermission(String permission) {
        return ContextCompat.checkSelfPermission(ALib.app(), permission) == PackageManager.PERMISSION_GRANTED;
    }

    public static List<String> checkPermissions(String... permissions) {
        List<String> badPerms = new ArrayList(permissions.length);
        for (String perm : permissions) {
            if (!hasPermission(perm)) {
                badPerms.add(perm);
            }
        }
        return badPerms;
    }

    @Nullable
    public static String[] getUsesPermissions(String pkgName) {
        Context app = ALib.app();
        PackageManager pm = app.getPackageManager();
        try {
            PackageInfo packageInfo = pm.getPackageInfo(pkgName, PackageManager.GET_PERMISSIONS);
            return packageInfo.requestedPermissions;
        } catch (NameNotFoundException e) {
            return null;
        }
    }

    /**
     * <p>这个signature的内容等同META-INFO/CERT.RSA<p/>
     * <p>实际上，这里的Signature是签名用的x509证书，也就是公钥，并不是真正的签名。</p>
     * <p>由于这个x509证书是安装并验证apk的时候用的，因此他在编译的时候就不能更改了。
     * 如果用户替换了这个文件,那么，由于CERT.SF是用户自己的私钥生成。用原来的证书无法验证，造成安装失败。
     * 因此，用户无法替换这个文件。
     * 从这个意义上说，这个文件相当于apk的签名，跟版本无关的一个签名。
     * </p>
     */
    @NonNull
    @SuppressLint("MissingPermission")
    public static final Signature[] getApkSigns() {
        return getApkSigns(ALib.app().getPackageName());
    }

    @Nullable
    @RequiresPermission(permission.QUERY_ALL_PACKAGES)
    public static final Signature[] getApkSigns(String pkgName) {
        Context app = ALib.app();
        PackageManager pm = app.getPackageManager();
        try {
            if (Build.VERSION.SDK_INT >= 28) {
                PackageInfo pi = pm.getPackageInfo(pkgName, PackageManager.GET_SIGNING_CERTIFICATES);
                try {
                    android.content.pm.SigningInfo signinfo = pi.signingInfo;
                    return signinfo.getApkContentsSigners();
                } catch (Throwable e) {
                    return pi.signatures;
                }
            } else {
                PackageInfo pi = pm.getPackageInfo(app.getPackageName(), PackageManager.GET_SIGNATURES);
                return pi.signatures;
            }
        } catch (NameNotFoundException e) {
            return null;
        }
    }

    @NonNull
    @SuppressLint("MissingPermission")
    public static final String getApkSign(HashUtil.HashType hashtype) {
        return getApkSign(hashtype, ALib.app().getPackageName());
    }

    @NonNull
    @RequiresPermission(permission.QUERY_ALL_PACKAGES)
    public static final String getApkSign(HashUtil.HashType hashtype, String pkgName) {
        if (hashtype != HashUtil.HashType.MD5 && hashtype != HashUtil.HashType.SHA1 && hashtype != HashUtil.HashType.SHA256) {
            throw new IllegalArgumentException("apk的签名只支持MD5/sha1/sha256");
        }
        Signature[] signs = getApkSigns(pkgName);
        byte[] bytes = signs[0].toByteArray();
        bytes = HashUtil.hash(hashtype, bytes);
        return ByteUtil.toHex(bytes);
    }

    @NonNull
    @SuppressLint("MissingPermission")
    public static final X509Certificate getApkSignCert() {
        return getApkSignCert(ALib.app().getPackageName());
    }

    @Nullable
    @RequiresPermission(permission.QUERY_ALL_PACKAGES)
    public static final X509Certificate getApkSignCert(String pkgName) {
        Signature[] signatures = getApkSigns(pkgName);
        if (signatures == null) {
            return null;
        }
        if (Core.isDebug() && signatures.length != 1) {
            throw new SecurityException("more than 1 singnatures found,size:" + signatures.length);
        }
        return KeyStoreUtil.loadX509Cert(new ByteArrayInputStream(signatures[0].toByteArray()));
    }
    // public static final ActivityManager.RunningAppProcessInfo getMyProcess(Context context) {
    //     int myPid = android.os.Process.myPid();
    //     ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    //     List<RunningAppProcessInfo> runningProcesses = activityManager.getRunningAppProcesses();
    //     if (runningProcesses != null) {
    //         for (ActivityManager.RunningAppProcessInfo process : runningProcesses) {
    //             if (process.pid == myPid) {
    //                 return process;
    //             }
    //         }
    //     }
    //     return null;
    // }

    /**
     * @return 获取屏幕分辨率信息
     */
    public static DisplayMetrics getDisplayMetrics() {
        return Resources.getSystem().getDisplayMetrics();
        //        return ALib.app().getResources().getDisplayMetrics();
    }

    public static long getVerCode() {
        PackageInfo pkgInfo = getPkgInfo();
        if (Build.VERSION.SDK_INT >= 28) {
            return pkgInfo.getLongVersionCode();
        }
        return pkgInfo.versionCode;
    }

    public static final Locale getOsLocale() {
        return Locale.getDefault();
    }

    public static final Locale getAppLocale() {
        Configuration conf = CtxUtil.getAppConfiguation();
        if (Build.VERSION.SDK_INT >= 24) {
            return conf.getLocales().get(0);
        } else {
            return conf.locale;
        }
    }

    /**
     * 很接近deviceId，但是如果是配置一样的手机，会返回相同的值。
     * 用于测试的时候近似作为deviceId；
     *
     * @format off
     */
    public static final String getDeviceHash() {
        String deviceInfo = new StringBuilder().append(Build.BOARD).append("#").append(Build.BRAND).append("#")
                // CPU_ABI，这个值和appp使用的so库是arm64-v8a还是armeabi-v7a有关，舍弃
                //.append(Build.CPU_ABI).append("#")
                .append(Build.DEVICE).append("#").append(Build.DISPLAY).append("#").append(Build.HOST).append("#")
                .append(Build.ID).append("#").append(Build.MANUFACTURER).append("#").append(Build.MODEL).append("#")
                .append(Build.PRODUCT).append("#").append(Build.TAGS).append("#").append(Build.TYPE).append("#")
                .append(Build.USER).append("#").toString();
        return HashUtil.md5ToHex(StrUtil.bytesUtf8(deviceInfo));
    }

    @SuppressLint("MissingPermission")
    public static final String getDeviceId() {
        if (Build.VERSION.SDK_INT >= 29) {
            // anroid10不允许获取IMEI
            return AndrMetaUtil.getAndroidId();
        }
        if (!AndrUtil.hasPermission(permission.READ_PHONE_STATE)) {
            return AndrMetaUtil.getAndroidId();
        }

        String deviceId = AndrMetaUtil.getIMEI();
        if (deviceId == null) {
            deviceId = AndrMetaUtil.getSN();
        }
        if (deviceId == null) {
            deviceId = AndrMetaUtil.getAndroidId();
        }
        return deviceId;
    }

    public static String getVerName() {
        PackageInfo pkgInfo = getPkgInfo();
        return pkgInfo.versionName;
    }


    //===================================================================
    //    public static final ComponentName getTopActivity() {
    //        ActivityManager manager = (ActivityManager) ALib.app()
    //                .getSystemService(Context.ACTIVITY_SERVICE);
    //        List<RunningTaskInfo> runningTaskInfos = manager.getRunningTasks(1);
    //        return runningTaskInfos.get(0).topActivity;
    //    }
    @Deprecated
    public static final void updateLocale(Locale locale) {
        if (Build.VERSION.SDK_INT >= 19) {
            if (locale == null) {
                Pref.def().remove("locale");
            } else {
                Pref.def().set("locale", locale.getISO3Language());
            }
            return;
        }
        if (locale == null) {
            return;
        }
        Resources resources = ALib.app().getResources();
        Configuration configuration = resources.getConfiguration();
        if (Build.VERSION.SDK_INT >= 17) {
            configuration.setLocale(locale);
        } else if (Build.VERSION.SDK_INT < 17) {
            configuration.locale = locale;
        }
        DisplayMetrics dm = resources.getDisplayMetrics();
        resources.updateConfiguration(configuration, dm);
    }

    @Deprecated
    public static final Context updateLocale19WhenAttachBaseContext(Context newBase, Locale newlocale) {
        if (Build.VERSION.SDK_INT >= 19) {
            Configuration configuration = CtxUtil.getAppConfiguation();

            if (Build.VERSION.SDK_INT >= 24) {
                LocaleList localeList = new LocaleList(newlocale);
                LocaleList.setDefault(localeList);
                configuration.setLocales(localeList);
            } else {
                configuration.setLocale(newlocale);
            }
            newBase = newBase.createConfigurationContext(configuration);
        }
        return newBase;
    }
    //===========================================================

    /**
     * SN: 唯一标志一台设备
     * IMEI=(IMEI for GSM or MEID or ESN for CDMA)只能支持拥有通话功能的设备，对于平板不可以。READ_PHONE_STATE权限<br/>
     * <br/>
     * MAC: 有些设备没有WiFi，或者蓝牙，就不可以，如果WiFi没有打开，硬件也不会返回Mac地址,ACCESS_WIFI_STATE权限<br/>
     * Android 6.0开始，谷歌为保护用户数据，用此方法获取到的 Wi-Fi mac 地址都为02:00:00:00:00:00<br/>
     * <br/>
     * IMSI=MCC[460]+MNC[01]+MSIN[1234567890]<br/>
     * IMSI储存在SIM卡的EF-IMSI文件中，可用于区别移动用户的有效信息。这个MSIN号是运营商在制卡中心制卡时已经唯一写入到卡中，可以区分每一张SIM卡。<br/>
     * <br/>
     * MSISDN=CC[86]+NDC[186]+SN[12345678]<br/>
     * MSISDN储存在SIM卡的EF-MSISDN文件中，<br/>
     * 但是该文件一般可以被更改，甚至可以被删除，所以一般不以该文件存储的内容为准。<br/>
     * 需要知道MSISDN号码，最常用的方法是拨出一个电话，看对方手机显示的是什么号码。<br/>
     * <br/>
     * IMSI是SIM卡的ID，MSISDN是用户的ID。意思就是，当用户去营业厅换SIM卡的时候，IMSI就变了，而MSISDN不变。<br/>
     *
     * @see AndrMetaUtil
     */
    @Deprecated
    @SuppressLint("MissingPermission")
    public static String getPhoneInfo(String key) {
        boolean hasPermission = AndrUtil.hasPermission(permission.READ_PHONE_STATE);
        if ("SN".equals(key)) {
            if (Build.VERSION.SDK_INT >= 26 && hasPermission) {
                return Build.getSerial();
            }
            return Build.SERIAL;
        } else if ("ANDROID_ID".equals(key)) {
            return android.provider.Settings.Secure
                    .getString(ALib.app().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
        } else if ("IMEI".equals(key)) {
            if (Build.VERSION.SDK_INT >= 29) {
                throw new ShouldNotRunHereException("anroid" + Build.VERSION.SDK_INT + "不允许获取IMEI,请用ANDROID_ID代替");
            }
            TelephonyManager tm = (TelephonyManager) ALib.app().getSystemService(Context.TELEPHONY_SERVICE);
            if (!hasPermission) {
                log.error("没有READ_PHONE_STATE权限,不能获取IMEI");
                return null;
            }
            if (Build.VERSION.SDK_INT >= 26) {
                return tm.getImei();
            }
            return tm.getDeviceId();
        } else if ("IMSI".equals(key)) {
            TelephonyManager tm = (TelephonyManager) ALib.app().getSystemService(Context.TELEPHONY_SERVICE);
            return tm.getSubscriberId();
        } else if ("PHONE_NO".equals(key)) {
            TelephonyManager tm = (TelephonyManager) ALib.app().getSystemService(Context.TELEPHONY_SERVICE);
            return tm.getLine1Number();
        } else if ("ICCID".equals(key)) {
            TelephonyManager tm = (TelephonyManager) ALib.app().getSystemService(Context.TELEPHONY_SERVICE);
            return tm.getSimSerialNumber();
        } else if ("MANUFACTURER".equals(key)) {
            return Build.MANUFACTURER;
        } else if ("MODEL".equals(key)) {
            return Build.MODEL;
        } else if ("BRAND".equals(key)) {
            return Build.BRAND;
        }
        throw new IllegalArgumentException("cant get info:" + key);
    }
}
